/*=============================================================================
# FileName    : nor_flash_core.v
# Author      : author
# Email       : email@email.com
# Description : 执行基本的总线操作, READ, WRITE, OUTPUT DISABLE, STANDBY, RESET
                28F00AP30仿真时写一个单位大概1400ns
                当前支持 BUS_ASYNC_READ, BUS_WRITE
# Version     : 1.0
# LastChange  : 2018-09-25 17:23:42
# ChangeLog   : 
=============================================================================*/
`timescale  1 ns/1 ps

module nor_flash_core #
(
    parameter               ADDR_W = 26,
    parameter               DATA_W = 16
)
(
    input   wire                clk,
    input   wire                rst,

    input   wire [07:00]        bus_command,
    input   wire [15:00]        bus_data,
    input   wire [ADDR_W-1:00]  bus_addr,
    output  wire                ready,    // 0 = busy; 1 = ready;
    output  wire                rd_data_valid, 
    output  wire [DATA_W-1:00]  rd_data,
    /*
     * nor flash 引脚
     */
    input   wire                nor_wait,

    output  wire                nor_rst_n,
    output  wire                nor_adv_n,    // address valid
    output  wire                nor_ce_n,     // chip enable
    output  wire                nor_oe_n,
    output  wire                nor_we_n,
    output  wire                nor_wp_n,   // write protect

    output  reg                 nor_clk,    // 异步模式可以不使用时钟信号
    output  wire [ADDR_W-1:00]  nor_addr,
    inout   wire [DATA_W-1:00]  nor_data    // 作为输入时，一定要将端口设置为高阻态
);
`include "nor_flash_macro.v"

//`define  BASIC_CMD_ASYNC_READ  16'h0001
//`define  BASIC_CMD_SYNC_READ   16'h0002
//`define  BASIC_CMD_WRITE       16'h0003
//`define  BASIC_CMD_DISABLE     16'h0004
//`define  BASIC_CMD_STANDBY     16'h0005
//`define  BASIC_CMD_RESET       16'h0006
//`define  BASIC_CMD_WR_DATA         16'h0007

localparam              IDLE            = 0;
localparam              BUS_ASYNC_READ  = 1;
localparam              BUS_SYNC_READ   = 2;
localparam              BUS_WRITE       = 3;
localparam              BUS_DISABLE     = 4;
localparam              BUS_STANDBY     = 5;
localparam              BUS_RESET       = 6;
localparam              OVER            = 8;

(* KEEP = "TRUE" *)reg     [DATA_W-1:00]             nor_data_out;
(* KEEP = "TRUE" *)wire    [DATA_W-1:00]             nor_data_in;
(* KEEP = "TRUE" *)wire                              nor_data_ctrl;     // 1, nor_data 输出数据
reg     [ADDR_W-1:00]             bus_addr_reg;
reg     [04:00]             dev_command;

(* KEEP = "TRUE" *)reg     [OVER:00]       cs = 'd1, ns = 'd1;
reg     [15:00]         state_cnt;

// synthesis translate_off
reg [127:0] cs_STRING;
always @(*)
begin
    case(1'b1)
        cs[IDLE]: cs_STRING = "IDLE";
        cs[BUS_ASYNC_READ]: cs_STRING = "BUS_ASYNC_READ";
        cs[BUS_SYNC_READ]: cs_STRING = "BUS_SYNC_READ";
        cs[BUS_WRITE]: cs_STRING = "BUS_WRITE";
        cs[BUS_STANDBY]: cs_STRING = "BUS_STANDBY";
        cs[BUS_RESET]: cs_STRING = "BUS_RESET";
        cs[OVER]: cs_STRING = "OVER";
        default: cs_STRING = "XXXX";
    endcase
end
// synthesis translate_on

always @(posedge clk)
begin
    if(rst)
        cs <= 'd1;
    else
        cs <= ns;
end

always @(*)
begin
    ns = 'd0;
    case(1'b1)
        cs[IDLE]:
        begin
            case (bus_command)
                `BASIC_CMD_ASYNC_READ: ns[BUS_ASYNC_READ] = 1'b1;
                `BASIC_CMD_SYNC_READ:  ns[BUS_SYNC_READ] = 1'b1;
                `BASIC_CMD_WRITE:      ns[BUS_WRITE] = 1'b1;
                `BASIC_CMD_STANDBY:    ns[BUS_STANDBY] = 1'b1;
                `BASIC_CMD_RESET:      ns[BUS_RESET] = 1'b1;
                default: ns[IDLE] = 1'b1;
            endcase
        end
        cs[BUS_ASYNC_READ]:
        begin
            if(state_cnt == 130)
                ns[IDLE] = 1'b1;
            else
                ns[BUS_ASYNC_READ] = 1'b1;
        end
        cs[BUS_WRITE]:
        begin
            if(state_cnt == 130)
                ns[IDLE] = 1'b1;
            else
                ns[BUS_WRITE] = 1'b1;
        end
        default:
            ns[IDLE] = 1'b1;
    endcase
end


always @ (posedge clk)
begin
    if(rst)
        state_cnt <= 0;
    else if (cs != ns)
        state_cnt <= 0;
    else
        state_cnt <= state_cnt + 1'b1;
end

always @ (posedge clk)
begin
    if(rst)
    begin
        dev_command <= 5'b10_111;
        //nor_rst_n <= 1;
        //nor_adv_n <= 1;
        //nor_ce_n <= 1;
        //nor_oe_n <= 1;
        //nor_we_n <= 1;
    end
    else
    begin
        case (1'b1)
            cs[BUS_ASYNC_READ]:
            begin
                if(state_cnt < 20)
                    dev_command <= 5'b10_111;
                else if( (state_cnt >= 20) & (state_cnt < 25))
                    dev_command <= 5'b10_011;
                else if( (state_cnt >= 25) & (state_cnt < 80))
                    dev_command <= 5'b10_001;
                else
                    dev_command <= 5'b10_111;
            end
            cs[BUS_WRITE]:
            begin
                if(state_cnt < 40)      // 默认状态
                    dev_command <= 5'b10_111;
                else if( (state_cnt >= 40) & (state_cnt < 60))
                    dev_command <= 5'b10_010;
                else if( (state_cnt >= 60) & (state_cnt < 80))
                    dev_command <= 5'b10_110;
                else
                    dev_command <= 5'b10_111;
            end
            default: dev_command <= 5'b10_111;
        endcase
    end
end

always @ (posedge clk)
begin
    if(rst)
    begin
        nor_data_out <= 0;
        bus_addr_reg <= 0;
    end
    else if (cs[IDLE] & (~ns[IDLE]) )
    begin
        nor_data_out <= bus_data;
        bus_addr_reg <= bus_addr;
    end
end

assign                  nor_wp_n = 1; 
assign                  nor_addr = bus_addr_reg;

assign                  ready = cs[IDLE];
assign                  {nor_rst_n, nor_adv_n, nor_ce_n, nor_oe_n, nor_we_n } = dev_command;

assign                  nor_data_ctrl = (cs[BUS_WRITE]);

`ifdef USE_IOBUF
assign                  nor_data = nor_data_ctrl ? nor_data_out : 16'hzzzz ;
assign                  nor_data_in = nor_data;
`else

// ug953
genvar i;
generate
    for(i=0;i<16;i=i+1)
    begin
        IOBUF #
        (
            .DRIVE           (  12                 ), // Specify the output drive strength
            .IBUF_LOW_PWR    (  "TRUE"             ), // Low Power - "TRUE", High Perforrmance = "FALSE"
            //.IOSTANDARD      (  "LVCMOS25"          ), // Specify the I/O standard
            .IOSTANDARD      (  "DEFAULT"          ), // Specify the I/O standard
            .SLEW            (  "SLOW"             )// Specify the output slew rate
        )
        IOBUF_Ex01
        (
            .I               (  nor_data_out[i]    ),
            .O               (  nor_data_in[i]     ),
            .T               (  ~nor_data_ctrl      ),
            .IO              (  nor_data[i]        )
        );
        /*
        * nor_data_ctrl 在往外写数据时为1，
        */
    end
endgenerate
`endif

assign                  rd_data_valid = (dev_command == 5'b10001);
assign                  rd_data = nor_data_in;

//IOBUF #
//(
    //.DRIVE(12), // Specify the output drive strength
    //.IBUF_LOW_PWR("TRUE"),  // Low Power - "TRUE", High Performance = "FALSE"
    //.IOSTANDARD("DEFAULT"), // Specify the I/O standard
    //.SLEW("SLOW") // Specify the output slew rate
//) 
//IOBUF_inst 
//(
    //.O(O_data_out),     // Buffer output
    //.IO(nor_data),   // Buffer inout port (connect directly to top-level port)
    //.I(nor_data_out),     // Buffer input
//　　.T((cs[BUS_ASYNC_READ] | cs[BUS_WRITE] ))      // 3-state enable input, high=input, low=output
//);
endmodule
